package com.ruoyi.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.domain.WarningConfig;
import com.ruoyi.business.domain.WarningConfigPush;
import com.ruoyi.business.domain.WarningConfigTo;
import com.ruoyi.business.domain.WarningHistory;
import com.ruoyi.business.domain.bo.WarningConfigBo;
import com.ruoyi.business.domain.vo.WarningConfigVo;
import com.ruoyi.business.mapper.*;
import com.ruoyi.business.service.IWarningService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.QueueConstant;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.dto.WarningPushDTO;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.event.PushMessageEvent;
import com.ruoyi.common.core.domain.event.WarningEvent;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 预警推送人配置Service业务层处理
 *
 * @author ruoyi
 * @date 2024-05-03
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WarningServiceImpl implements IWarningService {

    private final WarningConfigMapper configMapper;
    private final WarningHistoryMapper historyMapper;
    private final WarningConfigPushMapper pushMapper;
    private final WarningConfigToMapper toMapper;
    private final SysUserMapper sysUserMapper;

    /**
     * 记录预警事件
     *
     * @param event 预警事件
     */
    @Async
    @EventListener
    public void push(WarningEvent event) {
        log.info("预警--start:{}", event);
        //     先获取推送配置
        String type = event.getType();
        WarningConfig warningConfig = configMapper.selectOne(Wrappers.<WarningConfig>lambdaQuery().eq(WarningConfig::getType, type));
        if (warningConfig == null) {
            log.error("预警--配置不存在:{}", event);
            return;
        }
        if (Constants.FAIL.equals(warningConfig.getStatus())) {
            log.error("预警--配置已停用:{}", event);
            return;
        }
        //     获取可推送人
        List<WarningPushDTO> pushToList = this.getPushTo(warningConfig, event);
        if (pushToList.isEmpty()) {
            return;
        }
        //     推送
        pushToList.forEach((item) -> {
            // 推送历史
            WarningHistory history = new WarningHistory();
            history.setWarningId(warningConfig.getId());
            history.setWarningToId(item.getPushRef());
            history.setOwnerId(event.getOwnerId());
            history.setOwnerType(event.getOwnerType());
            history.setType(event.getType());
            history.setWarningTime(new Date());
            int insert = historyMapper.insert(history);
            // 推送
            log.info("预警--推送:{}-{}", history, item.getPushTo());
            if (insert > 0) {
                Map<String, String> pushMap = new HashMap<>();
                pushMap.put(item.getPushChannel(), item.getPushTo());
                SpringUtils.context().publishEvent(PushMessageEvent.builder()
                    .pushRef(event.getType())
                    .pushTo(pushMap)
                    .pushTime(new Date())
                    .pushContent(event.getContent())
                    .build());
            }
        });

    }

    /**
     * 获取可推送人
     * <p>
     * 根据 责任链去推送，如推送历史中已经推送过了，就不再推送，找到其下级推送人
     *
     * @param warningConfig 预警配置
     * @return 推送目标 0 <br/> 格式: <br/> 推送渠道:推送目标 <br/> 如 <br/> QueueConstant.PUSH_SMS_QUEUE:手机号
     */
    private List<WarningPushDTO> getPushTo(WarningConfig warningConfig, WarningEvent event) {

        // 从根节点开始遍历，只要有任意节点满足条件即可
        WarningConfigTo unpushedNode = findUnpushedNode(warningConfig, event, 0L);

        // 如果没有找到任何可推送节点，则结束
        if (unpushedNode == null) {
            log.warn("预警--无节点:{}", event);
            return new ArrayList<>();
        }
        //     把预警岗位分拆成多个岗位
        String[] postIds = unpushedNode.getPostIds().split(",");
        if (postIds.length == 0) {
            log.error("预警--无岗位:{}", event);
            return new ArrayList<>();
        }

        // 查询岗位所有的用户
        List<SysUser> userList = sysUserMapper.selectByPostIds(postIds);
        if (userList.isEmpty()) {
            log.error("预警--无用户:{}", event);
            return new ArrayList<>();
        }
        // 获取预警推送类型
        List<WarningConfigPush> pushList = pushMapper.selectList(Wrappers.<WarningConfigPush>lambdaQuery().eq(WarningConfigPush::getWarningId, warningConfig.getId()));
        if (pushList.isEmpty()) {
            log.error("预警--无推送类型:{}", event);
            return new ArrayList<>();
        }
        // 组装推送
        List<WarningPushDTO> pushDTOList = new ArrayList<>();
        for (SysUser user : userList) {
            for (WarningConfigPush push : pushList) {
                if (push.getPushType().equals(QueueConstant.PUSH_SMS_QUEUE)) {
                    if (StringUtils.isNotEmpty(user.getPhonenumber())) {
                        pushDTOList.add(WarningPushDTO.builder()
                            .pushRef(unpushedNode.getToId())
                            .pushChannel(QueueConstant.PUSH_SMS_QUEUE)
                            .pushTo(user.getPhonenumber())
                            .build());
                    }
                }
            }
        }

        return pushDTOList;

    }


    /**
     * 找到没有被推送的那一级(即历史推送中没有的那一级)
     *
     * @param warningConfig
     * @param event
     * @param parentId
     * @return
     */
    private WarningConfigTo findUnpushedNode(WarningConfig warningConfig, WarningEvent event, Long parentId) {
        // 查询当前节点的推送记录
        WarningConfigTo warningConfigTo = toMapper.selectOne(Wrappers.<WarningConfigTo>lambdaQuery()
            .eq(WarningConfigTo::getWarningId, warningConfig.getId())
            .eq(WarningConfigTo::getParentId, parentId)
            .last("limit 1"));
        if (warningConfigTo == null) {
            return null;
        }

        // 获取当前节点推送历史
        List<WarningHistory> warningHistoryList = historyMapper.selectList(Wrappers.<WarningHistory>lambdaQuery()
            .eq(WarningHistory::getWarningId, warningConfig.getId())
            .eq(WarningHistory::getWarningToId, warningConfigTo.getToId())
            .eq(WarningHistory::getOwnerId, event.getOwnerId())
            .eq(WarningHistory::getOwnerType, event.getOwnerType())
            .eq(WarningHistory::getType, event.getType()));
        if (warningHistoryList.isEmpty()) {
            return warningConfigTo;
        }
        // 配置中岗位人数*最大推送次数=最大推送次数
        String[] postIds = warningConfigTo.getPostIds().split(",");
        long numberOfPeople = sysUserMapper.countByPostIds(postIds);
        int pushedTimes = warningHistoryList.size();

        if (pushedTimes < warningConfigTo.getMaxWarningNum() * numberOfPeople) {
            // 发现可推送节点，立即返回
            return warningConfigTo;
        }

        // 递归查找下级节点
        return this.findUnpushedNode(warningConfig, event, warningConfigTo.getToId());
    }


    /**
     * 查询预警推送人配置
     */
    @Override
    public WarningConfigVo queryById(Long id) {
        return configMapper.selectVoById(id);
    }

    /**
     * 查询预警推送人配置列表
     */
    @Override
    public TableDataInfo<WarningConfigVo> queryPageList(WarningConfigBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WarningConfig> lqw = buildQueryWrapper(bo);
        Page<WarningConfigVo> result = configMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询预警推送人配置列表
     */
    @Override
    public List<WarningConfigVo> queryList(WarningConfigBo bo) {
        LambdaQueryWrapper<WarningConfig> lqw = buildQueryWrapper(bo);
        return configMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WarningConfig> buildQueryWrapper(WarningConfigBo bo) {
        LambdaQueryWrapper<WarningConfig> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getCron()), WarningConfig::getCron, bo.getCron());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), WarningConfig::getStatus, bo.getStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getType()), WarningConfig::getType, bo.getType());
        return lqw;
    }

    /**
     * 新增预警推送人配置
     */
    @Override
    public Boolean insertByBo(WarningConfigBo bo) {
        WarningConfig add = BeanUtil.toBean(bo, WarningConfig.class);
        validEntityBeforeSave(add);
        boolean flag = configMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改预警推送人配置
     */
    @Override
    public Boolean updateByBo(WarningConfigBo bo) {
        WarningConfig update = BeanUtil.toBean(bo, WarningConfig.class);
        validEntityBeforeSave(update);
        return configMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(WarningConfig entity) {
        // TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除预警推送人配置
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验,判断是否需要校验
        }
        return configMapper.deleteBatchIds(ids) > 0;
    }
}
